function _combs(hand) {
	let combs = [], sets = hand.melds.map(x => [x[0], x[1]])
	let table = new Array(39).fill(0)
	for (const tile of hand.tiles) {
		++table[getTileValue(tile)]
	}

	function dfs(requiredSets, requiredPairs, lastSet = 0) {
		if (!(requiredSets || requiredPairs)) {
			let comb = structuredClone(sets)
			comb.sort((a, b) => a[1] - b[1])
			combs.push(comb)
			return
		}
		if (requiredPairs) {
			// Search pairs
			for (let suit = 0; suit < 4; ++suit) {
				for (let tile = (suit === 3 ? 31 : (36 | suit)); tile > 3; tile -= 4)if (table[tile] >= 2) {
					table[tile] -= 2
					sets.push([CHOICE_CONCEALED_PAIR, tile])
					dfs(requiredSets, requiredPairs - 1)
					sets.pop()
					table[tile] += 2
				}
			}
		} else {
			// Search pungs
			if (lastSet < 80) for (let suit = 0; suit < 4; ++suit) {
				for (let tile = (suit === 3 ? 31 : (36 | suit)); tile > 3; tile -= 4)if (tile + 40 >= lastSet && table[tile] >= 3) {
					table[tile] -= 3
					sets.push([CHOICE_CONCEALED_PUNG, tile + 40])
					dfs(requiredSets - 1, 0, tile + 40)
					sets.pop()
					table[tile] += 3
				}
			}
			// Search chows
			for (let suit = 0; suit < 3; ++suit) {
				for (let tile = (28 | suit); tile > 3; tile -= 4)if (tile + 80 >= lastSet && table[tile] && table[tile + 4] && table[tile + 8]) {
					--table[tile]
					--table[tile + 4]
					--table[tile + 8]
					sets.push([CHOICE_CONCEALED_CHOW, tile + 80])
					dfs(requiredSets - 1, 0, tile + 80)
					sets.pop()
					++table[tile]
					++table[tile + 4]
					++table[tile + 8]
				}
			}
		}
	}

	dfs(4 - sets.length, 1)
	return combs
}